package net.disk;

import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;

import net.fusejna.DirectoryFiller;
import net.fusejna.ErrorCodes;
import net.fusejna.FuseException;
import net.fusejna.StructFuseFileInfo.FileInfoWrapper;
import net.fusejna.StructStat.StatWrapper;
import net.fusejna.types.TypeMode.ModeWrapper;
import net.fusejna.util.FuseFilesystemAdapterFull;

import com.dropbox.client2.DropboxAPI;
import com.google.api.services.drive.model.File;
import com.isunlive.kuaipan.modle.KuaipanFile;

public class NetDisk extends FuseFilesystemAdapterFull
{
	private static int GOOGLE = 0;
	private static int DROPBOX = 1;
	private static int KUAIPAN = 2;

	public static void main(final String... args) throws FuseException
	{
		if (args.length != 1) {
			System.err.println("Usage: NetDisk <mountpoint>");
			System.exit(1);
		}
		new NetDisk().log(true).mount(args[0]);
	}

	private final GoogleAccess google;
	private final DropBoxAccess dropbox;
	private final KuaipanAccess kuaipan;
	private final Netdirectory root;
	private String state;
	private String create;
	String basepath = "/home/richardcypher/netdisk";
	private int storechoice;

	public NetDisk()
	{
		google = new GoogleAccess();
		dropbox = new DropBoxAccess();
		kuaipan = new KuaipanAccess();
		root = new Netdirectory("");
		root.setID(google.folderid());
		addGoogleChildrenTo(root, "/");
		addDropBoxChildrenTo("/");
		addKuaipanChildrenTo("/");
		System.out.println("finished");
		state = "normal";
		create = "";
		storechoice = GOOGLE;
	}

	@Override
	public int access(final String path, final int access)
	{
		return 0;
	}

	/*
	 * Netpath相当于是一个文件系统的结构。有两个类继承它，一个是netdirectory，另一个是netfile。 netdirectory相当于文件夹，netfile相当于文件。
	 * 在构造函数里先新建了一个root的netdirectory，然后通过调用这个函数来将网盘中的所有文件都加入到这个文件系统中。 这里的函数也只有parent.add（）。
	 * 每次要把一个文件或者文件夹加入时，先新建，然后调用parent.add将他加入。 注意两个类的构造函数，一个是文件夹名和parent； 另一个是文件名和文件内容 (String类型 *
	 * 我是通过google的api下载文件的内容，不管怎么样，你们把文件内容转换成String然后穿进去就可以了) 这里你们可能要自己再写两个函数， 分别把dropbox和金山的东西家进去。然后在构造函数里面调用一下。
	 * 注意把文件是从哪个网盘下载的设置好。用setFrom(google/dropbox/kuaipan);
	 */
	/* files in dropbox */
	public void addDropBoxChildrenTo(final String path)
	{
		try {
			final List<DropboxAPI.Entry> files = dropbox.queryChildren(path);
			for (int i = 0; i < files.size(); i++) {
				final InputStream in = dropbox.downloadFile(files.get(i).path);
				final byte[] bytes = Util.streamToBytes(in);
				final Netfile nfile = new Netfile(files.get(i).fileName(), new String(bytes));
				nfile.setFrom("dropbox");
				((Netdirectory) getParentPath(files.get(i).path)).add(nfile);
			}
		}
		catch (final Exception e) {
			e.printStackTrace();
		}
	}

	public void addGoogleChildrenTo(final Netdirectory parent, final String path)
	{// to get all the files from the net disk
		/* files in google drive */
		try {
			final List<File> files = google.queryChildren(path);
			for (final File file : files) {
				if (file.getMimeType().equals("application/vnd.google-apps.folder")) {
					final Netdirectory directory = new Netdirectory(file.getTitle(), parent);
					addGoogleChildrenTo(directory, path + directory.name + "/");
					directory.setID(file.getId());
					parent.add(directory);
					// System.out.println(file.toPrettyString());
				}
				else {
					final InputStream in = google.downloadFile(file);
					final byte[] bytes = new byte[in.available()];
					in.read(bytes);
					final Netfile nfile = new Netfile(file.getTitle(), new String(bytes));
					nfile.setID(file.getId());
					nfile.setFrom("google");
					parent.add(nfile);
					// System.out.println(file.toPrettyString());
				}
			}
		}
		catch (final IOException e) {
			e.printStackTrace();
		}
	}

	/* files in kuaipan */
	public void addKuaipanChildrenTo(final String path)
	{
		List<KuaipanFile> files = new ArrayList<KuaipanFile>();
		try {
			files = kuaipan.queryChildren(path, files);
			for (int i = 0; i < files.size(); i++) {
				final Netfile nfile = new Netfile(files.get(i).name, kuaipan.downloadFile(files.get(i).path));
				nfile.setFrom("kuaipan");
				((Netdirectory) getParentPath(files.get(i).path)).add(nfile);
			}
		}
		catch (final Exception e) {
			e.printStackTrace();
		}
	}

	@Override
	public int create(final String path, final ModeWrapper mode, final FileInfoWrapper info)
	{
		if (getPath(path) != null) {
			return -ErrorCodes.EEXIST;
		}
		final Netpath parent = getParentPath(path);
		if (parent instanceof Netdirectory) {
			((Netdirectory) parent).mkfile(getLastComponent(path));
			state = "create";
			create = path;
			return 0;
		}
		return -ErrorCodes.ENOSYS;
	}

	@Override
	public int getattr(final String path, final StatWrapper stat)
	{
		final Netpath p = getPath(path);
		if (p != null) {
			p.getattr(stat);
			return 0;
		}
		return -ErrorCodes.ENOENT;
	}

	private String getLastComponent(String path)
	{
		while (path.substring(path.length() - 1).equals("/")) {
			path = path.substring(0, path.length() - 1);
		}
		if (path.isEmpty()) {
			return "";
		}
		return path.substring(path.lastIndexOf("/") + 1);
	}

	private Netpath getParentPath(final String path)
	{
		return root.find(path.substring(0, path.lastIndexOf("/")));
	}

	private Netpath getPath(final String path)
	{
		return root.find(path);
	}

	@Override
	public int mkdir(final String path, final ModeWrapper mode)
	{
		if (getPath(path) != null) {
			return -ErrorCodes.EEXIST;
		}
		final Netpath parent = getParentPath(path);
		if (parent instanceof Netdirectory) {
			((Netdirectory) parent).mkdir(getLastComponent(path));
			/* 下面是新建文件夹的过程 ，在这里要在三个网盘都新建文件夹 */
			final File file1 = google.insertFile(path.substring(path.lastIndexOf("/") + 1), path, parent.getID(),
					"application/vnd.google-apps.folder", basepath + path, true);
			// 下面是我用到的id，你们用不到可以不用管
			final Netdirectory file2 = (Netdirectory) getPath(path);
			file2.setID(file1.getId());
			// dropbox
			dropbox.createFolder(path);
			// kuaipan
			kuaipan.createFolder(path);
			return 0;
		}
		return -ErrorCodes.ENOENT;
	}

	@Override
	public int open(final String path, final FileInfoWrapper info)
	{
		return 0;
	}

	@Override
	public int read(final String path, final ByteBuffer buffer, final long size, final long offset, final FileInfoWrapper info)
	{
		final Netpath p = getPath(path);
		if (p == null) {
			return -ErrorCodes.ENOENT;
		}
		if (!(p instanceof Netfile)) {
			return -ErrorCodes.EISDIR;
		}
		return ((Netfile) p).read(buffer, size, offset);
	}

	@Override
	public int readdir(final String path, final DirectoryFiller filler)
	{
		final Netpath p = getPath(path);
		if (p == null) {
			return -ErrorCodes.ENOENT;
		}
		if (!(p instanceof Netdirectory)) {
			return -ErrorCodes.ENOTDIR;
		}
		((Netdirectory) p).read(filler);
		return 0;
	}

	@Override
	public int release(final String path, final FileInfoWrapper info)
	{
		final Netpath parent = getParentPath(path);
		if (state == "create" && create.equals(path)) {// 这是新建的时候调用api在云盘上建文件的过程,这只是建了一个空文件，默认在google上面建，
			// 如果空间不够可以删掉他，再在其他网盘上新建文件。
			state = "normal";
			create = "";
			if (storechoice == GOOGLE) {
				System.out.println("upload to google");
				final File file1 = google.insertFile(path.substring(path.lastIndexOf("/") + 1), path, parent.getID(),
						"text/plain", basepath + path, false);
				final Netfile file2 = (Netfile) getPath(path);
				file2.setFrom("google");
				file2.setID(file1.getId());
			}
			else if (storechoice == DROPBOX) {
				System.out.println("upload to dropbox + " + path);
				final java.io.File file = new java.io.File(basepath + path);
				dropbox.uploadFile(file, path);
				final Netfile file2 = (Netfile) getPath(path);
				file2.setFrom("dropbox");
			}
			else if (storechoice == KUAIPAN) {
				System.out.println("upload to kuaipan");
				final java.io.File file = new java.io.File(basepath + path);
				kuaipan.uploadFile(file, path);
				final Netfile file2 = (Netfile) getPath(path);
				file2.setFrom("kuaipan");
			}
			storechoice++;
			storechoice %= 3;
		}
		else if (state == "write") {// 这是修改文件的过程,先判断是哪个网盘的文件，然后如果发现空间不够就找一个空间够的网盘存起来，然后把他从原来的网盘删掉；
			// 如果空间够就直接调用网盘的api将他update
			state = "normal";
			final Netfile wfile = (Netfile) getPath(path);
			/*
			 * 找出空间足够的一个网盘。 这里省略查找的过程
			 */
			int schedulechoice = -1;
			boolean movefile = false;
			if ((google.totalbytes() - google.usedbytes() > dropbox.totalbytes() - dropbox.usedbytes())
					&& (google.totalbytes() - google.usedbytes() > kuaipan.totalbytes() - kuaipan.usedbytes())) {
				schedulechoice = GOOGLE;
			}
			else if ((dropbox.totalbytes() - dropbox.usedbytes() > google.totalbytes() - google.usedbytes())
					&& (dropbox.totalbytes() - dropbox.usedbytes() > kuaipan.totalbytes() - kuaipan.usedbytes())) {
				schedulechoice = DROPBOX;
			}
			else if ((kuaipan.totalbytes() - kuaipan.usedbytes() > google.totalbytes() - google.usedbytes())
					&& (kuaipan.totalbytes() - kuaipan.usedbytes() > dropbox.totalbytes() - dropbox.usedbytes())) {
				schedulechoice = KUAIPAN;
			}
			if (wfile.getFrom().equals("google")) {
				if (google.usedbytes() + wfile.content.length() > google.totalbytes()) {// google空间不足
					google.deleteFile(wfile.getID());
					// 这里将文件存储到找出的网盘内。
					movefile = true;
				}
				else {// google空间足够
					try {
						final File file = google.queryFile(path);
						google.updateFile(file.getId(), file.getTitle(), file.getDescription(), file.getMimeType(), basepath
								+ path, false);
					}
					catch (final IOException e) {
						e.printStackTrace();
					}
				}
			}
			else if (wfile.getFrom().equals("dropbox")) {
				System.out.println(dropbox.usedbytes());
				System.out.println(wfile.content.length());
				System.out.println(dropbox.totalbytes());
				if (dropbox.usedbytes() + wfile.content.length() > dropbox.totalbytes()) {
					dropbox.delete(path);
					movefile = true;
				}
				else {
					final java.io.File file = new java.io.File(basepath + path);
					System.out.println("hello: " + path);
					dropbox.updateFile(file, path);
				}
			}
			else if (wfile.getFrom().equals("kuaipan")) {
				if (kuaipan.usedbytes() + wfile.content.length() > kuaipan.totalbytes()) {
					kuaipan.delete(path);
					movefile = true;
				}
				else {
					final java.io.File file = new java.io.File(basepath + path);
					kuaipan.updateFile(file, path);
				}
			}
			if (movefile) {
				if (schedulechoice == GOOGLE) {
					final File file1 = google.insertFile(path.substring(path.lastIndexOf("/") + 1), path, parent.getID(),
							"text/plain", basepath + path, false);
					final Netfile file2 = (Netfile) getPath(path);
					file2.setID(file1.getId());
					file2.setFrom("google");
				}
				else if (schedulechoice == DROPBOX) {
					final java.io.File file = new java.io.File(path);
					dropbox.uploadFile(file, path);
					final Netfile file2 = (Netfile) getPath(path);
					file2.setFrom("dropbox");
				}
				else if (schedulechoice == KUAIPAN) {
					final java.io.File file = new java.io.File(path);
					kuaipan.uploadFile(file, path);
					final Netfile file2 = (Netfile) getPath(path);
					file2.setFrom("kuaipan");
				}
			}
		}
		return 0;
	}

	@Override
	public int releasedir(final String path, final FileInfoWrapper info)
	{
		return 0;
	}

	@Override
	public int rename(final String path, final String newName)
	{
		final Netpath p = getPath(path);
		if (p == null) {
			return -ErrorCodes.ENOENT;
		}
		final Netpath newParent = getParentPath(newName);
		if (newParent == null) {
			return -ErrorCodes.ENOENT;
		}
		if (!(newParent instanceof Netdirectory)) {
			return -ErrorCodes.ENOTDIR;
		}
		final String oldname = path.substring(0, path.lastIndexOf("/") + 1);
		final String nname = newName.substring(0, path.lastIndexOf("/") + 1);
		if (!oldname.equals(nname)) {// 移动
			if (p instanceof Netfile) {// 要移动的是文件
				if (p.getFrom().equals("google")) {
					google.moveFile(p.getID(), newParent.getID());
				}
				else if (p.getFrom().equals("dropbox")) {
					dropbox.moveFile(path, newName);
				}
				else if (p.getFrom().equals("kuaipan")) {
					kuaipan.moveFile(path, newName);
				}
			}
			else if (p instanceof Netdirectory) {// 移动的是文件夹，三个网盘都移动
				google.moveFile(p.getID(), newParent.getID());
				// dropbox
				dropbox.moveFile(path, newName);
				// kuaipan
				kuaipan.moveFile(path, newName);
			}
		}
		else {// 重命名
			if (p instanceof Netfile) {
				if (p.getFrom().equals("google")) {
					google.RenameFile(p.getID(), newName.substring(newName.lastIndexOf("/") + 1));
				}
				else if (p.getFrom().equals("dropbox")) {
					dropbox.renameFile(path, newName);
				}
				else if (p.getFrom().equals("kuaipan")) {
					kuaipan.renameFile(path, newName);
				}
			}
			else if (p instanceof Netdirectory) {
				google.RenameFile(p.getID(), newName.substring(newName.lastIndexOf("/") + 1));
				dropbox.renameFile(path, newName);
				kuaipan.renameFile(path, newName);
			}
		}
		p.delete();
		p.rename(newName.substring(newName.lastIndexOf("/")));
		((Netdirectory) newParent).add(p);
		return 0;
	}

	@Override
	public int rmdir(final String path)
	{
		final Netpath p = getPath(path);
		if (p == null) {
			return -ErrorCodes.ENOENT;
		}
		if (!(p instanceof Netdirectory)) {
			return -ErrorCodes.ENOTDIR;
		}
		// 删除三个网盘中的文件夹
		google.trashFile(p.getID());
		// dropbox
		dropbox.delete(path);
		// kuaipan
		kuaipan.delete(path);
		p.delete();
		return 0;
	}

	@Override
	public int unlink(final String path)
	{
		final Netpath p = getPath(path);
		if (p == null) {
			return -ErrorCodes.ENOENT;
		}
		p.delete();
		if (p.getFrom().equals("google")) {
			google.trashFile(p.getID());
		}
		else if (p.getFrom().equals("dropbox")) {
			dropbox.delete(path);
		}
		else if (p.getFrom().equals("kuaipan")) {
			kuaipan.delete(path);
		}
		return 0;
	}

	@Override
	public int write(final String path, final ByteBuffer buf, final long bufSize, final long writeOffset,
			final FileInfoWrapper wrapper)
	{
		final Netpath p = getPath(path);
		if (p == null) {
			return -ErrorCodes.ENOENT;
		}
		if (!(p instanceof Netfile)) {
			return -ErrorCodes.EISDIR;
		}
		if (state == "create") {
		}
		else {
			state = "write";
		}
		return ((Netfile) p).write(buf, bufSize, writeOffset);
	}
}
